package com.xyy.saas.payment.service.auth.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.pojo.bo.*;
import com.xyy.saas.payment.service.auth.AuthService;
import com.xyy.saas.payment.constants.Constants;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.dao.model.MenuInfoPo;
import com.xyy.saas.payment.dao.model.MenuPermissionPo;
import com.xyy.saas.payment.dao.model.PayUserPo;
import com.xyy.saas.payment.service.MenuInfoService;
import com.xyy.saas.payment.service.MenuPermissionService;
import com.xyy.saas.payment.service.PayUserService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.HttpClientUtil;
import com.xyy.saas.payment.util.JSONUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wangtianqi
 * @date 2020/5/`1`5 15:54
 */
@Service
@DependsOn("apolloUtil")
public class AuthServiceImpl implements AuthService, InitializingBean {

    private final static Logger LOGGER = LoggerFactory.getLogger(AuthServiceImpl.class);

    @Value("${xyy.sso.baseUrl}")
    private String baseUrl;

    @Value("${sso.casServerUrl}")
    private String casServiceHost;

    @Autowired
    private PayUserService payUserService;

    @Autowired
    private MenuPermissionService menuPermissionService;

    @Autowired
    private MenuInfoService menuInfoService;

    @Autowired
    private RedisClient redisClient;

    private final static String QUERY_USER_BY_ACCOUNT = "/api/user/queryUserByAccount";

    private final static String GET_USER = "/v2/api/user/getCurrentLoggedUser";

    private List<String> whiteList = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_ACCOUNT_WHITE_LIST), String.class);

    private RestTemplate restTemplate;

    public SSOUser querySSOUser(String account, String tgc) throws PaymentException {
        String casServerDomain = null;
        try {
            casServerDomain = new URL(this.casServiceHost).getHost();
        } catch (MalformedURLException e) {
            throw new IllegalStateException("sso.casServiceHost字段异常");
        }

        String url = this.baseUrl + QUERY_USER_BY_ACCOUNT + "?account=" + account;
        LOGGER.info("查询sso用户信息.path:{}", url);
        try {

            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("TGC", tgc);
            HttpEntity<String> requestEntity = new HttpEntity<String>(null, requestHeaders);
            ResponseEntity<SSOQueryUserResponse> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, SSOQueryUserResponse.class);

            if (response.getStatusCode() != HttpStatus.OK) {
                throw new PaymentException("查询登录用户异常,原因：请求失败!");
            }

            SSOQueryUserResponse queryUserResponse = response.getBody();

            if (queryUserResponse.getCode() != 200) {
                LOGGER.error("查询登录用户异常，url:{} tgc:{}",url,tgc);
                throw new PaymentException("查询登录用户异常,原因：" + queryUserResponse.getMsg());
            }
            return queryUserResponse.getResult();
        } catch (PaymentException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("查询中台用户信息异常", e);
            throw new PaymentException("查询中台用户信息异常", e);
        }
    }

    public String queryUserAccount(String tgc) throws PaymentException {
        String casServerDomain = null;
        try {
            casServerDomain = new URL(this.casServiceHost).getHost();
        } catch (MalformedURLException e) {
            throw new IllegalStateException("sso.casServiceHost字段异常");
        }

        String url = this.baseUrl + GET_USER;
        LOGGER.info("查询sso用户信息.path:{}", url);
        try {

            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.add("TGC", tgc);
            requestHeaders.add("sourceSysCode", "31");
            HttpEntity<String> requestEntity = new HttpEntity<String>("", requestHeaders);

//            ResponseEntity<SSOResponse> response = restTemplate.exchange(url, HttpMethod.GET, requestEntity, SSOResponse.class);

            Map<String, String> headers = new HashMap<>(4);
            headers.put("TGC", tgc);
            headers.put("sourceSysCode", "31");
            String response = HttpClientUtil.xyyHttpGetRequest(url, new HashMap<>(2) , headers);
//            if (response.getStatusCode() != HttpStatus.OK) {
//                throw new PaymentException("查询登录用户异常,原因：请求失败!");
//            }

            SSOResponse queryUserResponse = JSONUtils.parse2Object(response, SSOResponse.class);
            if (queryUserResponse.getCode() != 200) {
                LOGGER.error("查询登录用户异常，url:{} tgc:{}",url,tgc);
                throw new PaymentException("查询登录用户异常,原因：" + queryUserResponse.getMsg());
            }


            SSOUser ssoUser = JSON.parseObject(((JSONObject)queryUserResponse.getResult()).toJSONString(), SSOUser.class);
            return ssoUser.getAccount();
        } catch (PaymentException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("查询中台用户信息异常", e);
            throw new PaymentException("查询中台用户信息异常", e);
        }
    }



    public Boolean checkTGC(String account, String TGC) throws PaymentException {
        if(redisClient.exists(TGC)) {
            return true;
        }
        SSOUser ssoUser = querySSOUser(account, TGC);
        if (ssoUser == null) {
            return false;
        }
        redisClient.setEx(TGC, JSON.toJSONString(ssoUser), Constants.REDIS_USER_TOKEN_EXPIRE);
        return true;
    }

    @Override
    public String checkWhiteList(String tgc) throws PaymentException {
       String account = queryUserAccount(tgc);
       if(account != null) {
           LOGGER.info("AuthServiceImpl#checkWhiteList Principal:{}", account);
           return account;
       } else {
           throw new PaymentException(ResultCodeEnum.DEFAULT_ERROR);
       }
    }

    @Override
    public void getDataAndMenu(QueryAccountBo queryAccountBo, String type) throws PaymentException {
        try {
            //获取数据权限及菜单权限
            PayUserPo userInfo = payUserService.getUserByAccount(queryAccountBo.getAccount());
            if (null != userInfo) {
                if ("login".equals(type) && userInfo.getActive() == 1){
                    throw new PaymentException("9011","账号未启用");
                }
                MenuPermissionPo menuPermissionPo = menuPermissionService.getMenuByAccount(queryAccountBo.getAccount());

                queryAccountBo.setDataPermission(userInfo.getDataPermission());
                if (null != menuPermissionPo) {
                    if ("login".equals(type)) {
                        //将菜单数据分组处理
                        String ids = "";
                        if (StringUtils.isEmpty(menuPermissionPo.getHarf())) {
                            ids = menuPermissionPo.getMenuId();
                        } else {
                            ids = menuPermissionPo.getMenuId() + "," + menuPermissionPo.getHarf();
                        }
                        List<String> menuIds = Arrays.asList(ids.split(","));
                        List<MenuInfoPo> menuInfoPos = menuInfoService.getMenuByMenuId(menuIds);
                        List<MenuBo> totalMennu = convertPoToBo(menuInfoPos);
                        List<MenuBo> menuBos = groupMenu(totalMennu);
                        queryAccountBo.setMenuPermission(menuBos);
                    }
                    if ("singal".equals(type)) {
                        queryAccountBo.setMenuLine(menuPermissionPo.getMenuId());
                        queryAccountBo.setHarf(menuPermissionPo.getHarf());
                    }
                }

                if ("login".equals(type)) {
                    //更新最后登陆时间
                    PayUserPo login = new PayUserPo();
                    login.setUserAccount(queryAccountBo.getAccount());
                    login.setLastLoginTime(new Date());
                    payUserService.updateUser(login);
                }
            }
        }catch (PaymentException e) {
            throw e;
        }catch (Exception e){
            LOGGER.error("查询用户权限信息异常:{}", e);
            throw new PaymentException("查询用户权限信息异常",e);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Set set = new HashSet();
        set.add(Constants.APOLLO_KEY_ACCOUNT_WHITE_LIST);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                whiteList = JSONUtils.parse2List(ApolloUtil.getProperty(Constants.APOLLO_KEY_ACCOUNT_WHITE_LIST), String.class);
                LOGGER.info("AuthServiceImpl#onChange change whiteList  success whiteList:{} " , JSONUtils.toJSON(whiteList));
            }
        }, set);
    }

    private List<MenuBo> groupMenu(List<MenuBo> menuBos){
        List<MenuBo> finalMenu = new ArrayList<>();
        Map<String, List<MenuBo>> groupBos = menuBos.stream().filter((MenuBo item)->item.getParentId() != null).collect(Collectors.groupingBy(MenuBo::getParentId));
        for (MenuBo item:menuBos) {
            if(CollectionUtils.isNotEmpty(groupBos.get(item.getId()))){
                item.setChildren(groupBos.get(item.getId()));
            }
            if (StringUtils.isEmpty(item.getParentId())){
                finalMenu.add(item);
            }
        }
        return finalMenu;
    }

    private List<MenuBo> convertPoToBo(List<MenuInfoPo> menuInfoPos){
        List<MenuBo> menuBos = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(menuInfoPos)){
            menuInfoPos.forEach(po -> {
                MenuBo bo = new MenuBo();
                bo.setId(po.getMenuId());
                bo.setName(po.getName());
                bo.setParentId(po.getParentId());
                bo.setUrl(po.getUrl());
                menuBos.add(bo);
            });
        }
        return menuBos;
    }
}
